package eval.engine.openStack;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.google.inject.Inject;
import eval.deploy.DeploySet;
import eval.deploy.IDemandManager;
import eval.deploy.IDeployManager;
import eval.engine.BasicEngineModel;
import eval.engine.IPlacement;
import eval.engine.IPlacementEngine;
import eval.host.Host;
import eval.host.IHostManager;
import eval.host.compare.ISortHostList;
import eval.hostEvaluate.IHostEvaluate;
import eval.vm.IVMManager;
import eval.vm.MinCPURequiredComparator;
import eval.vm.VirtualMachine;

/**
 * This class is used to get the previous tick cloud state,
 * update it as needed (e.g. remove any VM which finish it life cycle),
 * than deploy the new set of VM's if needed,
 * and transfer the new status to the OPEN_STACK PLACEMENT. 
 * 
 * @author Amit Peled   <amit.kapina@gmail.com>
 * @author Igal Kaufman <igal.kaufman@gmail.com> 
 */
public class OpenStackEngine extends BasicEngineModel implements IPlacementEngine{


	/**
	 * @Constructor
	 * @param vmManager
	 * @param deployManager
	 * @param hostManager
	 * @param demandManager
	 * @param sortHostList
	 */
	@Inject
	public OpenStackEngine(IVMManager vmManager, IDeployManager deployManager,
			IHostManager hostManager, IDemandManager demandManager,
			ISortHostList sortHostList,IHostEvaluate hostEvaluator) {
		super(vmManager,deployManager,hostManager,demandManager,sortHostList,hostEvaluator);
	}

	/* (non-Javadoc)
	 * @see eval.engine.IPlacementEngine#computePlacement(int, eval.engine.IPlacement)
	 */
	@Override
	public IPlacement computePlacement(int tick, IPlacement lastPlacement) {
		//update start time
		engineStartTime = System.currentTimeMillis();
		this.tick =tick;
		//list of vm's that going to be deploy  
		List<VirtualMachine> placedNewVms =  new ArrayList<VirtualMachine>();
		//list of vm's that ask to deploy but rejected.
		List<VirtualMachine> rejectedVms =  new ArrayList<VirtualMachine>();
		//remove vm's list
		List<VirtualMachine> vmsToRemove = this.deployManager.getVMToBeRemovedAtTick(tick ,vmMapping);
		//deploy new vm list
		DeploySet deploySet = deployManager.getDeploySetAtTick(tick);
		//new deploy vm's
		List<VirtualMachine> vmsToDeploy = deploySet.getVMs();
		//CPU demand of vm's already on cloud
		Map<VirtualMachine, Integer> cpuDemandOfVMs = demandManager.getCpuDemandOfAllVMsAtTick(tick);
		//MEM demand of vm's already on cloud
		Map<VirtualMachine, Integer> memDemandOfVMs = demandManager.getMemDemandOfAllVMsAtTick(tick);
		//remove vm from host
		removeVirtualMachines(vmsToRemove);
		//Initial vmMaping and cloud
		iniCloudMapping(lastPlacement,vmsToRemove,cpuDemandOfVMs,memDemandOfVMs);
		//validatePlacement();
		if (!deploySet.isEmpty()) {  
			//we need to deploy new vm's
			// Order in which VMs are considered
			int vmCpuLoad,vmMemLOad;
			Collections.sort(vmsToDeploy,Collections.reverseOrder(new MinCPURequiredComparator()));
			for (VirtualMachine vm : vmsToDeploy) {
				Host host = getFirstFitHost(vm,cpuDemandOfVMs,memDemandOfVMs,tick);
				if(host != null){
					vmCpuLoad = Math.min(cpuDemandOfVMs.get(vm).intValue(),vm.getVMConfiguration().getMinCpu());
					vmMemLOad = memDemandOfVMs.get(vm).intValue();
					vmManager.setSatisfiedLoad(vm, vmCpuLoad, vmMemLOad);
					hostManager.addVmToHost(host,vm,vmCpuLoad, vmMemLOad);
					deployManager.setVmStartLife(vm, tick);
					placedNewVms.add(vm);
				}
				else{
					rejectedVms.add(vm);
				}
			}

		}
		//update demand
		updateLoadOnCloud(cpuDemandOfVMs,memDemandOfVMs);
		//update map
		updateVmMap();
		HistoryDemandArray[tick] = getTotalCpuDemand(rejectedVms);
		//check if we need to open/close host
		if(rejectedVms.size() != 0){
			hostManager.openNewHost(tick);
		}
		if(!openNewHost())
			closeHost();
		validatePlacement();
		if(vmsToDeploy.size() != placedNewVms.size() +rejectedVms.size())
			System.out.println("Error at tick: "+tick);
		openStackPlacement newPlacement = new openStackPlacement(
				vmMapping, hostManager, lastPlacement, demandManager ,vmsToRemove,
				vmsToDeploy,placedNewVms,rejectedVms ,tick , calcCloudGraphGradient());
		//update end time
		engineStopTime = System.currentTimeMillis();
		return newPlacement;
	}


	/* (non-Javadoc)
	 * @see eval.engine.IPlacementEngine#getEngineTimeInSec()
	 */
	@Override
	public double getEngineTimeInSec() {
		return engineStopTime - engineStartTime;
	}

	/* (non-Javadoc)
	 * @see eval.engine.IPlacementEngine#resetEngineTime()
	 */
	@Override
	public void resetEngineTime() {
		engineStartTime = engineStopTime = 0;
	}

}
